/* Copyright 2007-2008 dnAnalytics Project.
 *
 * Contributors to this file:
 * Patrick van der Velde
 * Marcus Cuda
 * Jurgen Van Gael
 *
 * This file is part of dnAnalytics.  dnAnalytics is licensed under the 
 * Microsoft Public License. See License.txt for a complete copy of the
 * license.
 */

using System;
using System.Collections.Generic;
using dnAnalytics.Properties;
using dnAnalytics.Math;

namespace dnAnalytics.LinearAlgebra
{
    public partial class SparseVector
    {
        /// <summary>
        /// The array containing the actual values; only the non-zero values are stored.
        /// </summary>
        private double[] mValues = new double[0];

        /// <summary>
        /// The indices of the non-zero entries.
        /// </summary>
        private int[] mIndices = new int[0];

        /// <summary>
        /// The number of values stored in the values array.
        /// </summary>
        internal int mValueCount;

        /// <summary>
        /// Creates instance of the <see cref="SparseVector" /> class.
        /// </summary>
        /// <param name="source">The <see cref="SparseVector"/> from which the values for the current instance will be copied.</param>
        /// <exception cref="ArgumentNullException">if source is <b>null</b>.</exception>
        public SparseVector(SparseVector source) : base(source.Count)
        {
            mValueCount = source.mValueCount;

            mValues = new double[source.mValues.Length];
            Buffer.BlockCopy(source.mValues, 0, mValues, 0, source.mValues.Length * Constants.SizeOfDouble);

            mIndices = new int[source.mIndices.Length];
            Buffer.BlockCopy(source.mIndices, 0, mIndices, 0, source.mIndices.Length * Constants.SizeOfInt);
        }

        #region Locate a vector item in the arrays

        /// <summary>
        /// Locates the position of the given item.
        /// </summary>
        /// <param name="index">The index of the requested item.</param>
        /// <param name="itemIndex">The index of the requested item in the internal array.</param>
        /// <returns><b>true</b> if the specified index exists; otherwise <b>false</b>.</returns>
        private bool FindItem(int index, out int itemIndex)
        {
            // First find the start and end indices for the row
            int startIndex = 0;
            int endIndex = mValueCount;

            // Check if startIndex and endIndex are equal. If so then
            // the item does not exist
            if (startIndex == endIndex)
            {
                itemIndex = endIndex;
                return false;
            }

            // Now subtract 1 from the endIndex because the current endIndex
            // indicates 1 past the end of the array
            endIndex -= 1;

            // If the startIndex is only one bigger than the endIndex
            // do a separate check because the binary search loop will not 
            // be entered.
            // Check if one of the boundaries is the correct index
            if (startIndex >= endIndex - 1)
            {
                return FindItemForSizeTwoArray(startIndex, endIndex, index, out itemIndex);
            }

            itemIndex = (startIndex + endIndex) / 2;
            // Start the binary search
            while (startIndex < endIndex - 1)
            {
                if (mIndices[itemIndex] == index)
                {
                    // Found the value we are looking for so we can exit
                    // here
                    return true;
                }

                // We're still here so we didn't find it. Now see in
                // what direction to search
                if (mIndices[itemIndex] > index)
                {
                    // value is to big that means that we need to move 
                    // back in the array
                    endIndex = itemIndex;
                }
                else
                {
                    // So the value must be to small
                    // Move forward in the array
                    startIndex = itemIndex;
                }
                itemIndex = (startIndex + endIndex) / 2;

                // Do a quick check to see if the end of the search was
                // reached. This occurs when itemIndex is equal to one
                // of the two boundaries. In this case this should always
                // be the lower end because integer division truncates instead
                // of rounding
                if (startIndex >= endIndex - 1)
                {
                    return FindItemForSizeTwoArray(startIndex, endIndex, index, out itemIndex);
                }
            }
            return false;
        }

        /// <summary>
        /// Performs a search for a specified item in an array of size two.
        /// </summary>
        /// <remarks>
        /// An array with size two is a special case when performing a binary search so this
        /// method is used to handle that special case.
        /// </remarks>
        private bool FindItemForSizeTwoArray(int startIndex, int endIndex, int columnIndex, out int itemIndex)
        {
            if ((startIndex > -1) && (startIndex < mIndices.Length) &&
                (mIndices[startIndex] == columnIndex))
            {
                itemIndex = startIndex;
                return true;
            }

            if ((endIndex > -1) && (endIndex < mIndices.Length) &&
                (mIndices[endIndex] == columnIndex))
            {
                itemIndex = endIndex;
                return true;
            }

            // neither one of the boundaries is actually correct
            // Check where eventual new values should be placed
            if (mIndices[startIndex] > columnIndex)
            {
                itemIndex = startIndex;
            }
            else
            {
                if (mIndices[endIndex] > columnIndex)
                {
                    itemIndex = endIndex;
                }
                else
                {
                    itemIndex = endIndex + 1;
                }
            }

            return false;
        }

        #endregion
        
        #region Resize the data storage arrays

        /// <summary>
        /// Increases the size of both the mValues and the mIndices array's.
        /// </summary>
        private void GrowStorage()
        {
            // If we already are at the maximum size then exit now
            if (mIndices.Length == Count)
            {
                return;
            }
            
            // Determine the increase in size. We will not grow beyond
            // the size of the vector
            int size = System.Math.Min(mValues.Length + GrowthSize(), Count);
            
            // Grow the valueArray by X%
            // Grow the columnIndex array by X%
            // Both arrays should always be equal in length!!!!!
            double[] newValues = new double[size];
            int[] newIndices = new int[size];
            // Copy the data to the new arrays.
            Buffer.BlockCopy(mValues, 0, newValues, 0, mValues.Length * Constants.SizeOfDouble);
            Buffer.BlockCopy(mIndices, 0, newIndices, 0, mIndices.Length * Constants.SizeOfInt);
            // Use the new arrays as the data blocks.
            mValues = newValues;
            mIndices = newIndices;
        }

        /// <summary>
        /// Calculates the amount with which to grow the storage array's if they need to be
        /// increased in size.
        /// </summary>
        private int GrowthSize()
        {
            int delta;
            if (mValues.Length > 1024)
            {
                delta = mValues.Length / 4;
            }
            else
            {
                if (mValues.Length > 256)
                {
                    delta = 512;
                }
                else
                {
                    delta = mValues.Length > 64 ? 128 : 32;
                }
            }
            return delta;
        }

        #endregion

        #region Internal add item to the vector

        /// <summary>
        /// Inserts an item into the array.
        /// </summary>
        private void InsertValue(int index, double value)
        {
            // Check if the value is zero. If it is then do nothing 
            if (!Precision.EqualsWithinDecimalPlaces(0.0, value, Constants.DecimalsForComparison))
            {
                // We assume that the item is not present in the array
                // First find the spot where the value must be inserted
                int itemIndex;
                FindItem(index, out itemIndex);

                // Check if the storage needs to be increased
                if (mValueCount == mValues.Length)
                {
                    // Value array is completely full so we increase the size
                    // This also grows the columIndex array
                    GrowStorage();
                }
                // Move all values (with an position larger than index) in the value array 
                // to the next position
                // move all values (with an position larger than index) in the columIndices 
                // array to the next position
                for (int i = mValueCount - 1; i > itemIndex - 1; i--)
                {
                    mValues[i + 1] = mValues[i];
                    mIndices[i + 1] = mIndices[i];
                }

                // Add the value and the column index
                mValues[itemIndex] = value;
                mIndices[itemIndex] = index;

                // increase the number of non-zero numbers by one
                mValueCount += 1;
            }
        }

        #endregion

        #region Internal delete value from vector

        /// <summary>
        /// Deletes an item from the vector.
        /// </summary>
        private void DeleteItem(int index)
        {
            // We have an item that needs to be removed from the
            // array. First check if it is actually stored in the array
            int itemIndex;
            if (FindItem(index, out itemIndex))
            {
                // Move all values (with an position larger than index) in the value array 
                // to the previous position
                // move all values (with an position larger than index) in the columIndices 
                // array to the previous position
                for (int i = itemIndex + 1; i < mValueCount; i++)
                {
                    mValues[i - 1] = mValues[i];
                    mIndices[i - 1] = mIndices[i];
                }

                mValueCount -= 1;
            }
        }

        #endregion

        /// <summary>Indexer gets or sets the value at given index.</summary>
        /// <param name="index">The index of the value to get or set.</param>
        /// <returns>The value of the vector at the given index.</returns>
        public override double this[int index]
        {
            get
            {
                if ((index < 0) || (index >= Count))
                {
                    throw new IndexOutOfRangeException();
                }

                int itemIndex;
                if (FindItem(index, out itemIndex))
                {
                    return mValues[itemIndex];
                }
                return 0.0;
            }
            set
            {
                if ((index < 0) || (index >= Count))
                {
                    throw new IndexOutOfRangeException();
                }

                int itemIndex;
                if (FindItem(index, out itemIndex))
                {
                    // Check if the value is zero.
                    if (Precision.EqualsWithinDecimalPlaces(0.0, value, Constants.DecimalsForComparison))
                    {
                        DeleteItem(index);
                    }
                    else
                    {
                        mValues[itemIndex] = value;
                    }
                }
                else
                {
                    InsertValue(index, value);
                }
            }
        }

        /// <summary>
        /// Returns the number of non zero elements in the vector.
        /// </summary>
        /// <value>The number of non zero elements.</value>
        public int NonZeros
        {
            get
            {
                return mValueCount;
            }
        }

        /// <summary>
        /// Calculates the infinity norm of this vector.
        /// </summary>
        /// <returns>The infinity norm of this vector.</returns>
        public override double InfinityNorm()
        {
            double ret = 0;
            for (int i = 0; i < mValueCount; i++)
            {
                ret = System.Math.Max(ret, System.Math.Abs(mValues[i]));
            }
            return ret;
        }

        /// <summary>
        /// Computes the P-Norm of the vector.
        /// </summary>
        /// <param name="pValue">The norm to compute.</param>
        /// <returns></returns>
        public override double PNorm(int pValue)
        {
            if (pValue < 1)
            {
                throw new ArgumentOutOfRangeException("pValue", Resources.NotPositive);
            }
            if (pValue == 1)
            {
                double ret = 0;
                for (int i = 0; i < mValueCount; i++)
                {
                    ret += System.Math.Abs(mValues[i]);
                }
                return ret;
            }
            else
            {
                double ret = 0;
                for (int i = 0; i < mValueCount; i++)
                {
                    ret += System.Math.Pow(System.Math.Abs(mValues[i]), pValue);
                }
                return System.Math.Pow(ret, 1.0 / pValue);
            }
        }

        /// <summary>
        /// Returns the value of the absolute maximum element.
        /// </summary>
        /// <returns>the value of the absolute maximum element.</returns>
        public override double AbsoluteMaximum()
        {
            int internalIndex;
            if (FindItem(AbsoluteMaximumIndex(), out internalIndex))
            {
                return System.Math.Abs(mValues[internalIndex]);
            }
            // The element has to be zero because the index was not found.
            return 0.0;
        }

        /// <summary>
        /// Returns the index of the absolute maximum element.
        /// </summary>
        /// <returns>The index of absolute maximum element if it exists; otherwise -1.</returns>
        public override int AbsoluteMaximumIndex()
        {
            int index = -1;
            double max = 0.0;
            for (int i = 1; i < mValueCount; i++)
            {
                double test = System.Math.Abs(mValues[i]);
                if (test > max)
                {
                    index = mIndices[i];
                    max = test;
                }
            }
            return index;
        }

        /// <summary>
        /// Returns the value of the absolute minimum element.
        /// </summary>
        /// <returns>The value of the absolute minimum element.</returns>
        public override double AbsoluteMinimum()
        {
            int internalIndex;
            if (FindItem(AbsoluteMinimumIndex(), out internalIndex))
            {
                return System.Math.Abs(mValues[internalIndex]);
            }

            // The element has to be zero because the index was not found.
            return 0.0;
        }

        /// <summary>
        /// Returns the index of the absolute minimum element.
        /// </summary>
        /// <remarks>
        /// Normally the smallest absolute minimum element will have a value of zero. If
        /// there are more elements with this value this method will return the first
        /// element with that value.
        /// </remarks>
        /// <returns>The index of absolute minimum element.</returns>
        public override int AbsoluteMinimumIndex()
        {
            if (Count > mValueCount)
            {
                // The smallest value is 0.0.
                // Find the first element that is zero.
                int lastIndex = 0;
                for (int i = 0; i < mValueCount; i++)
                {
                    // If there is more than 1 between the
                    // two indices that means that there is an
                    // index missing. Thus that has to be an index
                    // where the vector has value 0.0
                    if (mIndices[i] != lastIndex)
                    {
                        return lastIndex;
                    }

                    lastIndex++;
                }
            }
            else
            {
                // The vector is completely full thus there must be a
                // non-zero smallest value. Find it.
                int index = -1;
                double max = double.PositiveInfinity;
                for (int i = 1; i < mValueCount; i++)
                {
                    double test = System.Math.Abs(mValues[i]);
                    if (test < max)
                    {
                        index = mIndices[i];
                        max = test;
                    }
                }
                return index;
            }

            // Return value to fool the compiler. The if .. then .. else
            // statement should always return a value. However the compiler
            // is not convinced of that.
            return -1;
        }

        /// <summary>
        /// Returns the value of maximum element.
        /// </summary>
        /// <returns>The value of maximum element.</returns>
        public override double Maximum()
        {
            int internalIndex;
            if (FindItem(MaximumIndex(), out internalIndex))
            {
                return mValues[internalIndex];
            }

            // The element has to be zero because the index was not found.
            return 0.0;
        }

        /// <summary>
        /// Returns the index of the absolute maximum element.
        /// </summary>
        /// <returns>The index of absolute maximum element.</returns>
        public override int MaximumIndex()
        {
            int index = -1;
            double max = double.NegativeInfinity;
            for (int i = 0; i < mValueCount; i++)
            {
                double test = mValues[i];
                if (test > max)
                {
                    index = mIndices[i];
                    max = test;
                }
            }
            return index;
        }

        /// <summary>
        /// Returns the value of the minimum element.
        /// </summary>
        /// <returns>The value of the minimum element.</returns>
        public override double Minimum()
        {
            int internalIndex;
            if (FindItem(MinimumIndex(), out internalIndex))
            {
                return mValues[internalIndex];
            }

            // The element has to be zero because the index was not found.
            return 0.0;
        }

        /// <summary>
        /// Returns the index of the minimum element.
        /// </summary>
        /// <returns>The index of minimum element.</returns>
        public override int MinimumIndex()
        {
            int index = -1;
            double max = double.PositiveInfinity;
            for (int i = 0; i < mValueCount; i++)
            {
                double test = mValues[i];
                if (test < max)
                {
                    index = mIndices[i];
                    max = test;
                }
            }
            return index;
        }

        /// <summary>
        /// Sets the each element to zero.
        /// </summary>
        public override void Clear()
        {
            mValueCount = 0;
        }

        /// <summary>
        /// Copies the values of this vector into the target vector.
        /// </summary>
        /// <param name="target">The vector to copy elements into.</param>
        /// <exception cref="ArgumentNullException">If target is <b>null</b>.</exception>
        /// <exception cref="dnAnalytics.NotConformableException">If target is not the same size as this vector.</exception>
        public override void CopyTo(Vector target)
        {
            if (target == null)
            {
                throw new ArgumentNullException("target");
            }
            if (Count != target.Count)
            {
                throw new NotConformableException("target", Resources.ParameterNotConformable);
            }
            if (ReferenceEquals(this, target))
            {
                return;
            }

            target.Clear();
            for (int i = 0; i < mValueCount; i++)
            {
                target[mIndices[i]] = mValues[i];
            }
        }

        ///<summary>
        /// Computes the dot product of this vector with itself.
        /// </summary>
        ///<returns>The dot product of this vector and itself.</returns>
        public override double DotProduct()
        {
            double result = 0;
            for (int i = 0; i < mValueCount; i++)
            {
                result += mValues[i] * mValues[i];
            }

            return result;
        }

        /// <summary>
        /// Computes the dot product of this vector with another.
        /// </summary>
        /// <param name="other">The vector to compute the dot product with.</param>
        /// <returns>The dot product of this vector and other.</returns>
        /// <exception cref="ArgumentNullException">If the other vector is <b>null</b>.</exception>
        /// <exception cref="dnAnalytics.NotConformableException">If this vector and <paramref name="other"/> are not the same size
        /// </exception>
        public override double DotProduct(Vector other)
        {
            if (other == null)
            {
                throw new ArgumentNullException("other");
            }

            if (other.Count != Count)
            {
                throw new NotConformableException("other", Resources.ParameterNotConformable);
            }

            double result = 0;
            for (int i = 0; i < mValueCount; i++)
            {
                result += mValues[i] * other[mIndices[i]];
            }

            return result;
        }

        /// <summary>
        /// Returns an <see cref="IEnumerator{T}"/> that contains the position and value of the element.
        /// </summary>
        /// <returns>
        /// An <see cref="IEnumerator{T}"/> over this vector that contains the position and value of each
        /// non-zero element.
        /// </returns>
        /// <remarks>The enumerator returns a <seealso cref="KeyValuePair{T,K}"/> with the key being the element index and the value
        /// being the value of the element at that index. For sparse vectors, the enumerator will exclude all elements
        /// with a zero value.</remarks>
        public override IEnumerable<KeyValuePair<int, double>> GetIndexedEnumerator()
        {
            // Iterate over the vector size and return each value.
            for (int i = 0; i < mValueCount; i++)
            {
                yield return new KeyValuePair<int, double>(mIndices[i], mValues[i]);
            }
        }

        /// <summary>
        /// Creates a vector containing specified elements.
        /// </summary>
        /// <param name="index">The first element to begin copying from.</param>
        /// <param name="length">The number of elements to copy.</param>
        /// <returns>A vector containing a copy of the specified elements.</returns>
        /// <exception cref="ArgumentOutOfRangeException"><list><item>If <paramref name="index"/> is not positive or
        /// greater than or equal to the size of the vector.</item>
        /// <item>If <paramref name="index"/> + <paramref name="length"/> is greater than or equal to the size of the vector.</item>
        /// </list></exception>
        /// <exception cref="ArgumentException">If <paramref name="length"/> is not positive.</exception>
        public override Vector GetSubVector(int index, int length)
        {
            if (index < 0 || index >= Count)
            {
                throw new ArgumentOutOfRangeException("index");
            }
            if (index + length > Count)
            {
                throw new ArgumentOutOfRangeException("index");
            }

            Vector ret = CreateVector(length);
            int startIndex = -1;
            int endIndex = -1;
            // Find the start index. If the index exists then we get the correct location
            // otherwise we get one element past the index
            // Either is correct is this case.
            FindItem(index, out startIndex);

            // Find the end index. If the index exists then we get the correct location
            // otherwise we get one element past the index
            // Either is correct is this case.
            FindItem(index + length, out endIndex);

            for (int i = startIndex; i < endIndex; i++)
            {
                ret[mIndices[i] - index] = mValues[i];
            }
            return ret;
        }
        
        /// <summary>
        /// Computes the sum of the elements of this vector.
        /// </summary>
        /// <returns>The sum of the elements of this vector.</returns>
        public override double Sum()
        {
            double ret = 0;
            for (int i = 0; i < mValueCount; i++)
            {
                ret += mValues[i];
            }
            return ret;
        }

        /// <summary>
        /// Computes the sum of the absolute value of the elements of this vector.
        /// </summary>
        /// <returns>The sum of the absolute value of the elements of this vector.</returns>
        public override double SumMagnitudes()
        {
            double ret = 0;
            for (int i = 0; i < mValueCount; i++)
            {
                ret += System.Math.Abs(mValues[i]);
            }
            return ret;
        }

        /// <summary>
        /// Returns the data contained in the vector as an array.
        /// </summary>
        /// <returns>The data as an array.</returns>
        public override double[] ToArray()
        {            
            double[] ret = new double[Count];
            for (int i = 0; i < mValueCount; i++)
            {
                ret[mIndices[i]] = mValues[i];
            }

            return ret;
        }

        /// <summary>
        /// Set the values of this vector to the given values.
        /// </summary>
        /// <param name="values">The array containing the values to use.</param>
        /// <exception cref="ArgumentNullException">If <paramref name="values"/> is <b>null</b>.</exception>
        /// <exception cref="NotConformableException">If <paramref name="values"/> is not the same size as this vector.</exception>
        public override void SetValues(double[] values)
        {
            if (values == null)
            {
                throw new ArgumentNullException("values");
            }

            if (values.Length != Count)
            {
                throw new NotConformableException("values", Resources.ParameterNotConformable);
            }

            // 'Clear' the array
            mValueCount = 0;
            // Add the values
            for (int i = 0; i < values.Length; i++)
            {
                this[i] = values[i];
            }
        }

        /// <summary>
        /// Adds a scaled vector to the current vector.
        /// </summary>
        /// <param name="scale">The scale factor with which to multiply the <paramref name="other"/> vector.</param>
        /// <param name="other">The vector that should be scaled and added.</param>
        /// <exception cref="ArgumentNullException">If the other vector is <see langword="null" />.</exception> 
        /// <exception cref="NotConformableException">If this vector and <paramref name="other"/> are not the same size.</exception>
        public override void AddScaledVector(double scale, Vector other)
        {
            if (other == null)
            {
                throw new ArgumentNullException("other");
            }

            if (Count != other.Count)
            {
                throw new NotConformableException("other", Resources.ParameterNotConformable);
            }

            var sparseVector = other as SparseVector;
            if (sparseVector != null)
            {
                for (int i = 0; i < sparseVector.mValueCount; i++)
                {
                    this[sparseVector.mIndices[i]] += scale * sparseVector.mValues[i];
                }
            }
            else
            {
                for (int i = 0; i < Count; i++)
                {
                    this[i] += scale * other[i];
                }
            }
        }

        /// <summary>
        /// Adds a scaled vector to the current vector.
        /// </summary>
        /// <param name="scale">The scale factor with which to multiply the <paramref name="other"/> vector.</param>
        /// <param name="other">The vector to add to this one.</param>
        /// <param name="result">The vector to store the result of the addition.</param>
        /// <exception cref="ArgumentNullException">If the other vector is <see langword="null" />.</exception> 
        /// <exception cref="ArgumentNullException">If the result vector is <see langword="null" />.</exception> 
        /// <exception cref="NotConformableException">If this vector and <paramref name="other"/> are not the same size.</exception>
        /// <exception cref="NotConformableException">If this vector and <paramref name="result"/> are not the same size.</exception>
        public override void AddScaledVector(double scale, Vector other, Vector result)
        {
            if (other == null)
            {
                throw new ArgumentNullException("other");
            }

            if (result == null)
            {
                throw new ArgumentNullException("result");
            }

            if (Count != other.Count)
            {
                throw new NotConformableException("other", Resources.ParameterNotConformable);
            }

            if (Count != result.Count)
            {
                throw new NotConformableException("result", Resources.ParameterNotConformable);
            }

            if (ReferenceEquals(this, result) || ReferenceEquals(other, result))
            {
                Vector tmp = result.CreateVector(result.Count);
                AddScaledVector(scale, other, tmp);
                tmp.CopyTo(result);
            }
            else
            {
                CopyTo(result);
                result.AddScaledVector(scale, other); 
            }
        }
       
        /// <summary>
        /// Pointwise multiplies this vector with another vector and stores the result into the result vector.
        /// </summary>
        /// <param name="other">The vector to pointwise multiply with this one.</param>
        /// <param name="result">The vector to store the result of the pointwise multiplication.</param>
        /// <exception cref="ArgumentNullException">If the other vector is <see langword="null" />.</exception> 
        /// <exception cref="ArgumentNullException">If the result vector is <see langword="null" />.</exception> 
        /// <exception cref="NotConformableException">If this vector and <paramref name="other"/> are not the same size.</exception>
        /// <exception cref="NotConformableException">If this vector and <paramref name="result"/> are not the same size.</exception>
        public override void PointwiseMultiply(Vector other, Vector result)
        {
            if (result == null)
            {
                throw new ArgumentNullException("result");
            }
            if (other == null)
            {
                throw new ArgumentNullException("other");
            }

            if (Count != other.Count)
            {
                throw new NotConformableException("other", Resources.ParameterNotConformable);
            }

            if (Count != result.Count)
            {
                throw new NotConformableException("result");
            }

            if (ReferenceEquals(this, result) || ReferenceEquals(other, result))
            {
                Vector tmp = result.CreateVector(result.Count);
                PointwiseMultiply(other, tmp);
                tmp.CopyTo(result);
            }
            else
            {
                // Clear the vector so that we only have to fill in the (few) values we have stored.
                result.Clear();    

                // Only perform the multiplication for the indices at which there are
                // non-zero values
                for (int i = 0; i < mValueCount; i++)
                {
                    int index = mIndices[i];
                    result[index] = mValues[i] * other[index];
                }
            }
        }

        /// <summary>
        /// Negates the values of this vector.
        /// </summary>
        public override void Negate()
        {
            for (int i = 0; i < mValueCount; i++)
            {
                mValues[i] *= -1;
            }
        }

        /// <summary>
        /// Multiplies this vector by scalar.
        /// </summary>
        /// <param name="scalar">The scalar to multiply this vector with.</param>
        public override void Multiply(double scalar)
        {
            for (int i = 0; i < mValueCount; i++)
            {
                mValues[i] *= scalar;
            }
        }

        /// <summary>
        /// Multiplies this vector with another vector (this x other) and returns the resulting matrix.
        /// </summary>
        /// <param name="rightSide">The vector to multiply the vector with.</param>
        /// <exception cref="ArgumentNullException">If <paramref value="rightSide"/> is <b>null</b>.</exception>
        public override Matrix Multiply(Vector rightSide)
        {
            if (rightSide == null)
            {
                throw new ArgumentNullException("rightSide");
            }

            // Storing the result as a sparse matrix because it's most likely that
            // we'll get one.
            SparseMatrix result = new SparseMatrix(Count, rightSide.Count);
            Multiply(rightSide, result);
            return result;
        }

        /// <summary>
        /// Multiplies this vector with another vector (this x other) and places the result into the result vector.
        /// </summary>
        /// <param name="rightSide">The vector to multiply the vector with.</param>
        /// <param name="result">The result of the multiplication.</param>
        /// <exception cref="ArgumentNullException">If <paramref value="rightSide"/> is <b>null</b>.</exception>
        /// <exception cref="ArgumentNullException">If the result matrix is <b>null</b>.</exception>
        /// <exception cref="dnAnalytics.NotConformableException">If result matrix does not have the correct dimensions.</exception>
        public override void Multiply(Vector rightSide, Matrix result)
        {
            if (rightSide == null)
            {
                throw new ArgumentNullException("rightSide");
            }
            if (result == null)
            {
                throw new ArgumentNullException("result");
            }
            if (result.Rows != Count || result.Columns != rightSide.Count)
            {
                throw new NotConformableException("result", Resources.ParameterNotConformable);
            }

            for (int i = 0; i < Count; i++)
            {
                for (int j = 0; j < rightSide.Count; j++)
                {
                    result.ValueAt(i, j, this[i] * rightSide[j]);
                }
            }
        }
    }
}
